#!/usr/bin/env python
# coding: utf-8

# In[ ]:





# In[9]:


# MC MR MP
NFR_TRANSITION = {
    "MST": {
(True, True, True): (0.9, 0.91, 0.9),
(True, True, False): (0.88, 0.93, 0.85),
(True, False, True): (0.92, 0.89, 0.92),
(True, False, False): (0.9, 0.91, 0.87),
(False, True, True): (0.85, 0.93, 0.88),
(False, True, False): (0.83, 0.95, 0.83),
(False, False, True): (0.87, 0.91, 0.9),
(False, False, False): (0.85, 0.93, 0.85),
    },
    "RT": {
(True, True, True): (0.86, 0.95, 0.82),
(True, True, False): (0.84, 0.97, 0.75),
(True, False, True): (0.88, 0.93, 0.84),
(True, False, False): (0.86, 0.95, 0.77),
(False, True, True): (0.73, 0.97, 0.8),
(False, True, False): (0.71, 0.99, 0.73),
(False, False, True): (0.75, 0.95, 0.82),
(False, False, False): (0.73, 0.97, 0.75),      
    },
}


# In[10]:


def get_states_dist(S):
    # Get prob dist from S1 to S8
    mc, mr, mp = S
    dist = []
    for i in [mc, 1-mc]:
        for j in [mr, 1-mr]:
            for k in [mp, 1-mp]:
                dist.append(i * j * k)
    return dist


# In[16]:


def P_NFR(S, A):
    # S: Belief of states MC,MR,MP
    # A: MST | RT
    dist = get_states_dist(S)
    p_nfr = [0, 0, 0]
    for i,p in enumerate(NFR_TRANSITION[A].values()):
        for j in range(3):
            p_nfr[j] += p[j] * dist[i]
    return p_nfr


# In[45]:


OBS_RANGES = {
    "BC": (0.7, 0.8),
    "ANL": (0.08, 0.4),
    "TTW": (0.7, 0.9),
}

OBS_TRANSITION = {
    "MST": {
        "BC": [0.8, 0.15, 0.05],
        "ANL": [0.06, 0.16, 0.78],
        "TTW": [0.83, 0.13, 0.04],
    },
    "RT":{
        "BC": [0.78, 0.16, 0.06],
        "ANL": [0.05, 0.15, 0.8],
        "TTW": [0.8, 0.15, 0.05],
    }
}

NS_OBS_TRANSITION = {
    "MST": {
        "BC": [0.72, 0.18, 0.1],
        "ANL": [0.12, 0.2, 0.68],
        "TTW": [0.67, 0.23, 0.1],
    },
    "RT":{
        "BC": [0.68, 0.2, 0.12],
        "ANL": [0.1, 0.18, 0.8],
        "TTW": [0.63, 0.25, 0.12],
    }
}


# In[21]:


def get_obs_ratio(obs, topo, obs_type):
    if topo == "MST":
        lb = 8 * 3
        hb = 58 * 3
    else:
        lb = 50 * 3
        hb = 83 * 3
    if obs_type == "BC":
        lb *= 20
        hb *= 30
    if obs_type == "TTW":
        lb *= 10
        hb *= 20
    return (obs - lb) / (hb - lb)


# In[30]:


def get_obs_pos(obs, topo, obs_type):
    pos = OBS_RANGES[obs_type]
    obs_ratio = get_obs_ratio(obs, topo, obs_type)
    if obs_ratio < pos[0]:
        return 0
    elif pos[0] <= obs_ratio < pos[1]:
        return 1
    else:
        return 2


# In[33]:


def get_obs_prob(obs, topo, obs_type):
    pos = get_obs_pos(obs, topo, obs_type)
    if pos == 0:
        return OBS_RANGES[obs_type][0]
    elif pos == 1:
        return OBS_RANGES[obs_type][1] - OBS_RANGES[obs_type][0]
    else:
        return 1 - OBS_RANGES[obs_type][1]


# In[36]:


def get_obs_nfr_prob(obs, topo, obs_type):
    pos = get_obs_pos(obs, topo, obs_type)
    return OBS_TRANSITION[topo][obs_type][pos]


# In[37]:


def P_O_NFR(O, topo):
    # O: bc, anl, ttw
    bc, anl, ttw = O
    #return [P_BC_MC(bc, topo), P_ANL_MR(anl, topo), P_TTW_MP(ttw, topo)]
    return [
        get_obs_nfr_prob(bc, topo, "BC"),
        get_obs_nfr_prob(anl, topo, "ANL"),
        get_obs_nfr_prob(ttw, topo, "TTW"),
    ]


# In[48]:


def P_O_wrong(O, topo):
    bc, anl, ttw = O
    return [get_obs_prob(bc, topo, "BC"), get_obs_prob(anl, topo, "ANL"), get_obs_prob(ttw, topo, "TTW")]


# In[52]:


obs_names = ["BC", "ANL", "TTW"]


# In[46]:


def get_obs_dist(S, topo):
    obs_trans = OBS_TRANSITION[topo]
    ns_obs_trans = NS_OBS_TRANSITION[topo]
    obs_dist = {
        "BC": [0, 0, 0],
        "ANL": [0, 0, 0],
        "TTW": [0, 0, 0],
    }
    for i in range(3):
        p_nfr = S[i]
        for j in range(3):
            obs_dist[obs_names[i]][j] = p_nfr * obs_trans[obs_names[i]][j] + (1 - p_nfr) * ns_obs_trans[obs_names[i]][j]
    return obs_dist


# In[55]:


def P_O(S, topo, O):
    obs_dist = get_obs_dist(S, topo)
    p_o = [0, 0, 0]
    for i in range(3):
        pos = get_obs_pos(O[i], topo, obs_names[i])
        p_o[i] = obs_dist[obs_names[i]][pos]
    return p_o


# In[57]:


def P_NFR_O(O, S, A):
    p_n = P_NFR(S,A)
    p_o_n = P_O_NFR(O,A)
    p_o = P_O(S,A,O)
    p = [0, 0, 0]
    for i in range(3):
        p[i] = p_n[i] * p_o_n[i] / p_o[i]
    return p


# In[65]:


# P_NFR_O([6000, 50, 2000], [0.9, 0.2, 0.9], "MST")


# In[ ]:
def Simulator(f, env, deviation_list, topology_list, bandwidths_list, links_list, times_list, step):
	while True:
    #Runs the MAPEK loop
		Run(f)

		active_links, bandwidth_consumption, write_time = nm.probe()

		#This records the data to be displayed on the plots
		step.append(env.now)
		bandwidths_list.append(bandwidth_consumption)
		deviation_list.append(nm.deviation)
		topology_list.append(nm.current_topology)
		links_list.append(active_links)
		times_list.append(write_time)

		#Print the data on the console
		#print("timestep:", env.now)
		#print("Selected topology:", nm.current_topology)
		#print("Active Links:", active_links)
		#print("Bandwidth Consumption:", bandwidth_consumption)
		#print("Time to Write Data:", write_time, "\n")

		yield env.timeout(1)




